#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>

#include <map>
#include <queue>
#include <string>
#include <iostream>
#include <fstream>
#include	<algorithm>
#include	<stdint.h>

using namespace std;
	const uint32_t IPITEM_LEN = 64 ;
	const uint32_t IPCHAR_LEN = 16;
	const uint32_t MAX_LINE_LEN = 4*1024;
	const uint32_t MAX_UIDIP_TOKEN_NUM  = 2;
	const uint32_t MAX_IPITEM_TOKEN_NUM = 7;
	const uint32_t MAX_IPITEM_SINA_TOKEN_NUM = 10;
	const uint32_t MAX_UIDIPCOUNT_TOKEN_NUM = 3;
	const uint32_t MAX_IPITEM_NUM = 1<<18 ;
	const uint32_t MAX_CDN_TOKEN_NUM = 3;

/**
 * @brief
 */
	struct IpItem {
		uint32_t i_start;
		uint32_t i_end;
		char start[IPCHAR_LEN];
		char end[IPCHAR_LEN];
		char country[IPITEM_LEN] ;
		char province[IPITEM_LEN];
		char city[IPITEM_LEN];
		char district[IPITEM_LEN];
		char isp[IPITEM_LEN];
		char type[IPITEM_LEN];
		char desc[IPITEM_LEN];
	};				// ----------  end of struct IpItem  ----------

static bool cmp(const IpItem & a , const  IpItem &  b )  ;
#define CHECK_RET(RET,F,S,MSG) \
    do { \
	if(RET < F || RET >= S) {\
	    return -1; \
	} \
    }while(0)

class IpArea
{
	public:

		// ====================  LIFECYCLE     =======================================
		//
		int init (string filename)
		{
			_ip = new IpItem[MAX_IPITEM_NUM] ;
			_length = LoadIpFile(filename.c_str(), &_ip[1]) ;
			Sort( &_ip[1], _length);
			cout	<< "IpArea Init() Finish " << _length  << endl;
			return 0;
		}
		int Inet_network(string str)
		{
			int a  = 0 ;
			a += atoi(str.c_str()) * 256 * 256 * 256;
			str = str.substr(str.find(".") +1);
			a += atoi(str.c_str()) * 256 * 256 ;
			str = str.substr(str.find(".") +1);
			a += atoi(str.c_str()) * 256 ;
			str = str.substr(str.find(".") +1);
			a += atoi(str.c_str()) ;
			return a;

		}

		/**
		 * @brief
		 * @return
		 */

		static bool cmp(const IpItem & a ,  const IpItem &  b )
		{
			if(a.i_start != b.i_start)
				return a.i_start < b.i_start ;
			else
				return a.i_end < b.i_end;
		}

		/**
		 * @brief
		 * @return
		 */
		void Sort (IpItem* ip ,  uint32_t length)
		{
			sort(ip, &ip[length], cmp);
		}
		/**
		 * @brief
		 * @return
		 */
		int  Search ( uint32_t key , int level , string& msg)
		{
			int index = _Search(&_ip[0], key, _length);

			return GetInfo(index, level, msg);
		}


		/**
		 * @brief
		 * @return
		 */
		int  Search ( string key , int level , string& msg )
		{
			int intKey = Inet_network(key.c_str());
			int index = _Search(&_ip[0], intKey, _length);
			return GetInfo(index, level, msg);
		}

		/**
		 * @brief
		 * @return
		 */
		int  Search ( const char* key )
		{
			int intKey = Inet_network(key);
			return _Search(&_ip[0], intKey, _length);
		}

		/**
		 * @brief
		 * @return
		 */
		int  GetInfo ( int index, int level, string& msg)
		{
			if(0 > index )
				return -1;

			switch ( level ) {

				case 1:
					msg.append(_ip[index].country);
					msg.append("|");
					break;

				case 2:
					msg.append(_ip[index].country);
					msg.append("|");
					msg.append(_ip[index].province);
					msg.append("|");
					break;

				case 3:
					msg.append(_ip[index].country);
					msg.append("|");
					msg.append(_ip[index].province);
					msg.append("|");
					msg.append(_ip[index].city);
					msg.append("|");
					break;

				case 4:
					msg.append(_ip[index].country);
					msg.append("|");
					msg.append(_ip[index].province);
					msg.append("|");
					msg.append(_ip[index].city);
					msg.append("|");
					msg.append(_ip[index].district);
					msg.append("|");
					break;
				case 5:
				//	msg.append(_ip[index].country);
					//msg.append("|");
					msg.append(_ip[index].province);
					msg.append("\t");
					msg.append(_ip[index].city);
					msg.append("\t");
				//	msg.append(_ip[index].district);
				//	msg.append("|");
					msg.append(_ip[index].isp);
					msg.append("\t");
					break;

				default:
					msg.append(_ip[index].country);
					msg.append("|");
					msg.append(_ip[index].province);
					msg.append("|");
					msg.append(_ip[index].city);
					msg.append("|");
					break;
			}				// -----  end switch  -----

			return 0;
		}

		int _Search ( IpItem* ip ,uint32_t key , uint32_t length)
		{
			int s = 1, mid = 0;
			while ( s <= length  )
			{
				mid =  s + ((length -s) >> 1 ) ;
				if( ip[mid].i_start <= key and ip[mid].i_end >= key){
					return mid;
				}else if (ip[mid].i_start > key)
				{
					length = mid-1;
				}else if (ip[mid].i_end < key){
					s = mid + 1 ;
				}
			}
			return -1;
		}

		/**
		 * @brief
		 */
#define	SET_ITEM(ITEM , BUF , RET  )	if( NULL != ITEM and '\0' != ITEM[0] ) { RET = snprintf( BUF, IPITEM_LEN, "%s", ITEM ); }   else  { RET = 0 ; }		//
		/**
		 * @brief
		 */
		int ip_info ( char *token[] ,  int token_num , IpItem * ip_item )
		{
			int ret = 0 ;
			for ( int i = 0; i < 2 ; i++) { //
				if ( NULL == token[i] ) {
//					cout << "IpItem token[ " << i << " ] is NULL " ;
					return -1;
				}
			}
			ip_item->i_start = atol(token[0]);
			ip_item->i_end = atol(token[1]);

			SET_ITEM( token[2], ip_item->country, ret) ;
			CHECK_RET(ret, 0, IPITEM_LEN, " token[2] country is error ") ;

			SET_ITEM( token[3], ip_item->province, ret) ;
			CHECK_RET(ret, 0, IPITEM_LEN, " token[3] province is error ") ;

			SET_ITEM( token[4], ip_item->city, ret) ;
			CHECK_RET(ret, 0, IPITEM_LEN, " token[4] city is error ") ;

			SET_ITEM( token[5], ip_item->district, ret) ;
			CHECK_RET(ret, 0, IPITEM_LEN, " token[5] district is error ") ;

			SET_ITEM( token[6], ip_item->isp, ret) ;
			CHECK_RET(ret, 0, IPITEM_LEN, " token[6] isp is error ") ;

			return 0;
		}

		/**
		 * @brief
		 */
		int LoadIpFile (const char* filename, IpItem* ip )
		{
			char buf[MAX_LINE_LEN], *token[MAX_IPITEM_TOKEN_NUM];
			int global_num = 0, num = 0, error_num = 0, index_num = 0, ret = 0;
			FILE* fp = fopen(filename , "r");
			while ( NULL != fgets(buf, MAX_LINE_LEN, fp) )
			{

				global_num++ ;
				if('\t' == buf[0] || '\r' == buf[0] || '\n' == buf[0])
				{
					//LOG(INFO) << "[UID is NULL]" << "\t"<< global_num << "\t|" << buf <<"|";
					error_num++;
					continue;
				}
				int len = strlen(buf);

				num = 0,token[num ++ ] = buf;

				for(int i = 0; i < len; ++ i)
				{
					if('\t' == buf[i] || '\r' == buf[i] || '\n' == buf[i])
					{
						if(num < MAX_IPITEM_TOKEN_NUM and '\t' == buf[i])
						{
							token[num ++] = buf + i + 1;
						}
						buf[i] = '\0';
					}
				}
				ret = ip_info(token, num, &ip[index_num]);
				if (ret == -1)
				{
					cout << " ip_info something is error | " << global_num  << " |" ;
					continue;
				}
				index_num++ ;
			}

			if ( fp != NULL ) {
				fclose(fp);
				fp = NULL;
			}
			return index_num;
		}

		/**
		 * @brief
		 * @return
		 */
		IpArea ( void )
		{
			_ip = NULL;
			_length = 0 ;
			return ;
		}

		/**
		 * @brief
		 * @return
		 */
		~IpArea ( void )
		{
			if (_ip != NULL )
			{
				delete[] _ip;
				_ip = NULL;
			}
			return ;
		}
		uint32_t _length ;
		IpItem* _ip ;


};


int main ( int argc, char *argv[] )
{
	if(argc < 3 )
	{
		
		printf ( "Please Enter 2 stage inputFile outoutFile \n" );
		printf ( "Such as \n ./a.out inputFile outoutFile \n" );
		exit(0) ; 
	}
	IpArea* ipArea = new IpArea();
	ipArea->init("./conf/Form_int_zh_gbk.ip");
	ifstream infile(argv[1],ios::in);
	ofstream outfile(argv[2],ios::out);
    outfile <<"Ip\tCount\tprovince\tcity\tisp"<<endl;

	int level = 5 ;
	string str;
	while(getline(infile,str)){
        string ip = str;
        string msg="";
        int found ;
        found = str.find("\t");
        if(found == string::npos){
            found = str.find(" ");
        }
        if(found == string::npos)
        {
            ip = str;
        }else{
        	ip = str.substr(0,found);
        }

        int index = ipArea->Search(ip, level, msg);
                outfile << str <<"\t" << msg << endl;
	}

	return EXIT_SUCCESS;
}				// ----------  end of function main  ----------
